/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                           License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//
//   * The name of the copyright holders may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/

/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/


#include <QtCore>
#include <QtCore/QCoreApplication>
#include <QImage>
#include <QColor>
#include <QString>
#include <stdio.h>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>
#include <cmath>
#include <math.h>
#include <stdlib.h>
#include "Correlation.h"


using namespace std;


/* ----- Function declaration ------------------------------------------------------------------------------------*/
void parseCommandLine(QStringList commands, int *image_sequence, bool *enable_step, int *enable_debug);
void loadImageSequence(int image_sequence, string pathNames[], string fileNames[]);
void initTrainingGui();
void initTrainingGui2();
void initGui();
void trackbarHandler(int pos);
void trackbarHandlerSpecial(int pos);
void onMouse(int event, int x, int y, int flags, void *param);


/* ----- Global variables ----------------------------------------------------------------------------------------*/
Correlation c;
IplImage *imgInFull;
RgbImage imgThresh0, imgThresh1, imgThresh2, imgThresh3;
RgbImage imgThresh0_ROI, imgThresh1_ROI, imgThresh2_ROI, imgThresh3_ROI;
int prevPos = 0;




/*
  This function handles the file and user input and output.

  This function calls the code which does analysis as needed. All analytical code exists as structured functions in
    the correlation class. All data needed to analyze an image and compute the strain field are also managed by the
    correlation class.
  */
int main(int argc, char *argv[]) {

    /* ----- Initialization --------------------------------------------------------------------------------------*/

    // Initialize
    QCoreApplication a(argc, argv);


    // Handle inputs from the command line
    QStringList commands = a.arguments();
    parseCommandLine(commands, &(c.image_sequence), &(c.enable_step), &(c.enable_debug));


    // Declare variables
    //   Notes:
    //     -pathNames[0] is reserved for the path for all output files; change this location to change where output files
    //        are stored
    //     -pathNames[1] is the image source folder for all images
    //     -Currently, the remaining elements of pathNames are not used; in the future, it is straightforward to allow
    //        image sequences accross multiple directories (but why would anyone do this?!?)
    string pathNames[1001];
    string fileNames[1000];


    // Determine image sequence
    loadImageSequence(c.image_sequence, pathNames, fileNames);


    // Initialize correlation object
    c.initObject();


    /* ----- Parameter Thresholding ------------------------------------------------------------------------------*/

    // Loop through the two steps of training: (1) Color thresholding, and (2) Geometric thresholding
    for(int i = 0; i < 2; ++i) {

        if(i == 0) {

            /* ----- Color Thresholding --------------------------------------------------------------------------*/

            // Initialize GUI output
            initTrainingGui();

            // Display first image
            imgInFull = cvCloneImage(c.imgInFull.imgp);
            onMouse(CV_EVENT_LBUTTONUP, c.ROI.width + c.ROI.x, c.ROI.height + c.ROI.y, 0, 0);  // This draws a rect;
                                                                                               // does not mod ROI
            cvShowImage("Input Image", imgInFull);

        } else if(i == 1) {

            /* ----- Blob Thresholding ---------------------------------------------------------------------------*/

            // Partially re-initialize the GUI for blob thresholding
            initTrainingGui2();

            // Display first image
            imgInFull = cvCloneImage(c.imgInFull.imgp);
            cvShowImage("Input Image", imgInFull);

        }

        // Allow user to adjust color thresholds to correctly extract the blobs
        int key = -1;
        while(key == -1) {

            // Run code which thresholds the image
            imgThresh0_ROI = c.subsetImage(imgThresh0);
            c.filterImage(imgThresh0_ROI.imgp);
            c.blobAnalysis();
            c.blobProbability();
            c.identifyDots();
            c.outputImage(false, true);
            cvShowImage("Image 1", c.imgProc.imgp);

            imgThresh1_ROI = c.subsetImage(imgThresh1);
            c.filterImage(imgThresh1_ROI.imgp);
            c.blobAnalysis();
            c.blobProbability();
            c.identifyDots();
            c.outputImage(false, true);
            cvShowImage("Image 2", c.imgProc.imgp);

            imgThresh2_ROI = c.subsetImage(imgThresh2);
            c.filterImage(imgThresh2_ROI.imgp);
            c.blobAnalysis();
            c.blobProbability();
            c.identifyDots();
            c.outputImage(false, true);
            cvShowImage("Image 3", c.imgProc.imgp);

            imgThresh3_ROI = c.subsetImage(imgThresh3);
            c.filterImage(imgThresh3_ROI.imgp);
            c.blobAnalysis();
            c.blobProbability();
            c.identifyDots();
            c.outputImage(false, true);
            cvShowImage("Image 4", c.imgProc.imgp);

            // Wait for a keypress
            key = cvWaitKey(100);
        }

    }

    // Terminate process of obtaining persistent parameters
    cvReleaseImage(&imgThresh0.imgp);
    cvReleaseImage(&imgThresh1.imgp);
    cvReleaseImage(&imgThresh2.imgp);
    cvReleaseImage(&imgThresh3.imgp);
    cvReleaseImage(&imgThresh0_ROI.imgp);
    cvReleaseImage(&imgThresh1_ROI.imgp);
    cvReleaseImage(&imgThresh2_ROI.imgp);
    cvReleaseImage(&imgThresh3_ROI.imgp);
    c.savePersistentValues();


    /* ----- Analyze Image Sequence ------------------------------------------------------------------------------*/

    // Initialize GUI
    initGui();


    // Call analyzeImage() for the reference image; store reference position field
    c.analyzeImage(pathNames[0], pathNames[1], fileNames[0], true);
    c.storePositionField( pathNames[0], true );
    c.storeStrainField(pathNames[0], true);


    // If step, debug, or debug2 are enabled, output image
    if(c.enable_step || c.enable_debug > 0) {
        c.outputImage(true);
        c.outputOverlay(true);
    }


    // Recursively call analyzeImage on each image; store displacement field (relative to reference position field)
    int i = 1;  // Begin with the second image
    while(fileNames[i] != "")
    {
        bool result = c.analyzeImage(pathNames[0], pathNames[1], fileNames[i], false);
        if( !result ) {
            ++i;
            continue;
        }
        c.storePositionField(pathNames[0], false);
        c.storeStrainField(pathNames[0], false);

        // Update image and pause so user can see analysis process
        if(c.enable_step || c.enable_debug == 1) {
            c.outputImage(true);
            c.outputOverlay(true);
        }
        if(c.enable_debug > 1) {
            c.outputImage(true);
            c.outputOverlay(true);
            std::cout << "Processing " << fileNames[i] << endl;
            std::cout << endl;
            std::cout << "Press any key > ";
            std::cout << endl;
        }

        ++i;
    }


    // Output image for display
    c.outputImage(true);
    c.outputOverlay(true);

    cout << "Program finished." << endl;
}








/*
  Parse input commands from the command-line
  */
void parseCommandLine(QStringList commands, int *image_sequence, bool *enable_step, int *enable_debug) {

    // Parse command-line input
    for(int i = 0; i < commands.size(); i++) {
        QString command_i = commands.at(i).toLower();
        if( QString::compare(command_i.left(4), "-is=") == 0 ) {
            *image_sequence = command_i.right( command_i.size() - 4 ).toInt();
        }
        if( QString::compare(command_i, "--step") == 0 ) {
            *enable_step = true;
        }
        if( QString::compare(command_i, "--enable-debug") == 0 ) {
            *enable_debug = 1;
        }
        if( QString::compare(command_i, "--enable-debug2") == 0 ) {
            *enable_debug = 2;
        }
    }

    return;
}







/*
  Load path locations and file names for the image sequence.

  Additionally, load a subset of the images into objects for the thresholding process.
  */
void loadImageSequence(int image_sequence, string pathNames[], string fileNames[]) {

    // Initialize
    pathNames[0] = "/home/administrator/Documents/correlate/Output_Files/";
    int whichTest = 0;
    for(int i = 0; i < 1000; ++i) {
        fileNames[i] = "";
    }

    // Switch image sequence
    if(image_sequence == 1)
    {
        pathNames[1] = "/home/administrator/Documents/testCorrelate/Sample_Images/";
        fileNames[0] = "IMG_3907.jpg";
        fileNames[1] = "IMG_3907_shifted.jpg";
    } else if(image_sequence == 2) {
        pathNames[1] = "/home/administrator/Documents/testCorrelate/Sample_Images/6_17_11_compact_tension/";
        fileNames[0] = "IMG_4100.jpg";
        fileNames[1] = "IMG_4101.jpg";
        fileNames[2] = "IMG_4102.jpg";
        fileNames[3] = "IMG_4103.jpg";
        fileNames[4] = "IMG_4104.jpg";
        fileNames[5] = "IMG_4105.jpg";
        fileNames[6] = "IMG_4106.jpg";
        fileNames[7] = "IMG_4107.jpg";
        fileNames[8] = "IMG_4108.jpg";
        fileNames[9] = "IMG_4109.jpg";
        fileNames[10] = "IMG_4110.jpg";
        fileNames[11] = "IMG_4111.jpg";
        fileNames[12] = "IMG_4112.jpg";
        fileNames[13] = "IMG_4113.jpg";
        fileNames[14] = "IMG_4114.jpg";
        fileNames[15] = "IMG_4115.jpg";
        fileNames[16] = "IMG_4116.jpg";
        fileNames[17] = "IMG_4117.jpg";
        fileNames[18] = "IMG_4118.jpg";
        fileNames[19] = "IMG_4119.jpg";
        fileNames[20] = "IMG_4120.jpg";
        fileNames[21] = "IMG_4121.jpg";
        fileNames[22] = "IMG_4122.jpg";
        fileNames[23] = "IMG_4123.jpg";
        fileNames[24] = "IMG_4124.jpg";
        fileNames[25] = "IMG_4125.jpg";
        fileNames[26] = "IMG_4126.jpg";
        fileNames[27] = "IMG_4127.jpg";
        fileNames[28] = "IMG_4128.jpg";
    } else if(image_sequence == 3) {
        pathNames[1] = "/home/administrator/Documents/testCorrelate/Sample_Images/6_17_11_compact_tension/";
        fileNames[0] = "IMG_4125.jpg";
        fileNames[1] = "IMG_4126.jpg";
        fileNames[2] = "IMG_4127.jpg";
    } else if(image_sequence == 4) {
        pathNames[1] = "/home/administrator/Documents/testCorrelate/Sample_Images/8_11_11_sinusoidal_test_1/";
        fileNames[0] = "IMG_4601.JPG";
        fileNames[1] = "IMG_4602.JPG";
        fileNames[2] = "IMG_4603.JPG";
        fileNames[3] = "IMG_4604.JPG";
        fileNames[4] = "IMG_4605.JPG";
        fileNames[5] = "IMG_4606.JPG";
        fileNames[6] = "IMG_4607.JPG";
        fileNames[7] = "IMG_4608.JPG";
        fileNames[8] = "IMG_4609.JPG";
        fileNames[9] = "IMG_4610.JPG";
        fileNames[10] = "IMG_4611.JPG";
        fileNames[11] = "IMG_4612.JPG";
        fileNames[12] = "IMG_4613.JPG";
        fileNames[13] = "IMG_4614.JPG";
        fileNames[14] = "IMG_4615.JPG";
        fileNames[15] = "IMG_4616.JPG";
        fileNames[16] = "IMG_4617.JPG";
        fileNames[17] = "IMG_4618.JPG";
        fileNames[18] = "IMG_4619.JPG";
        fileNames[19] = "IMG_4620.JPG";
        fileNames[20] = "IMG_4621.JPG";
        fileNames[21] = "IMG_4622.JPG";
        fileNames[22] = "IMG_4623.JPG";
        fileNames[23] = "IMG_4624.JPG";
    } else if(image_sequence == 5) {
        pathNames[1] = "/home/administrator/Documents/testCorrelate/Sample_Images/8_11_11_break/";
        fileNames[0] = "IMG_4702.JPG";
        fileNames[1] = "IMG_4703.JPG";
        fileNames[2] = "IMG_4704.JPG";
        fileNames[3] = "IMG_4705.JPG";
        fileNames[4] = "IMG_4706.JPG";
        fileNames[5] = "IMG_4707.JPG";
        fileNames[6] = "IMG_4708.JPG";
        fileNames[7] = "IMG_4709.JPG";
        fileNames[8] = "IMG_4710.JPG";
        fileNames[9] = "IMG_4711.JPG";
        fileNames[10] = "IMG_4712.JPG";
        fileNames[11] = "IMG_4713.JPG";
        fileNames[12] = "IMG_4714.JPG";
        fileNames[13] = "IMG_4715.JPG";
        fileNames[14] = "IMG_4716.JPG";
        fileNames[15] = "IMG_4717.JPG";
        fileNames[16] = "IMG_4718.JPG";
        fileNames[17] = "IMG_4719.JPG";
        fileNames[18] = "IMG_4720.JPG";
        fileNames[19] = "IMG_4721.JPG";
        fileNames[20] = "IMG_4722.JPG";
    } else if(image_sequence == 6) {
        pathNames[1] = "/home/administrator/Documents/testCorrelate/Sample_Images/6_17_11_compact_tension/";
        fileNames[0] = "IMG_4108.jpg";
        fileNames[1] = "IMG_4109.jpg";
        fileNames[2] = "IMG_4110.jpg";
        fileNames[3] = "IMG_4111.jpg";
        fileNames[4] = "IMG_4112.jpg";
        fileNames[5] = "IMG_4113.jpg";
        fileNames[6] = "IMG_4114.jpg";
        fileNames[7] = "IMG_4115.jpg";
        fileNames[8] = "IMG_4116.jpg";
        fileNames[9] = "IMG_4117.jpg";
        fileNames[10] = "IMG_4118.jpg";
        fileNames[11] = "IMG_4119.jpg";
        fileNames[12] = "IMG_4120.jpg";
        fileNames[13] = "IMG_4121.jpg";
        fileNames[14] = "IMG_4122.jpg";
        fileNames[15] = "IMG_4123.jpg";
        fileNames[16] = "IMG_4124.jpg";
        fileNames[17] = "IMG_4125.jpg";
        fileNames[18] = "IMG_4126.jpg";
        fileNames[19] = "IMG_4127.jpg";
        fileNames[20] = "IMG_4128.jpg";
    } else if(image_sequence >= 100) {

        // Load image sequences used specifically as test cases.
        //   Each test sequence is defined by a number, beginning with 100 and continuing
        whichTest = image_sequence - 100;

        pathNames[1] = "/home/administrator/Documents/test-correlate/Test_Data_Programmatic/";

        char fCharArray[40];
        for(int i = 0; i < 1000; ++i) {

            // Write the maximum number of file names which could exist. The algorithms will fail gracefully for
            //   non-existent files.
            fileNames[i] = "";
            sprintf(fCharArray, "test%uplt%u.bmp", whichTest, i);
            fileNames[i].append(fCharArray);
        }

    } else {
        // Default image sequence
        pathNames[1] = "/home/administrator/Documents/testCorrelate/Sample_Images/6_17_11_compact_tension/";
        fileNames[0] = "IMG_4071.jpg";
        fileNames[1] = "IMG_4072.jpg";
        fileNames[2] = "IMG_4073.jpg";
        fileNames[3] = "IMG_4074.jpg";
        fileNames[4] = "IMG_4075.jpg";
        fileNames[5] = "IMG_4076.jpg";
        fileNames[6] = "IMG_4077.jpg";
        fileNames[7] = "IMG_4078.jpg";
        fileNames[8] = "IMG_4079.jpg";
        fileNames[9] = "IMG_4080.jpg";
        fileNames[10] = "IMG_4081.jpg";
        fileNames[11] = "IMG_4082.jpg";
    }

    // Load subset of images for thresholding procedure
    string sourcePathName = pathNames[1];
    string fileName = fileNames[0];
    string file = sourcePathName.append(fileName);
    c.imgInFull.imgp = cvLoadImage(file.c_str());

    // Determine number of images in sequence
    int s = -1;
    while(true) {

        ++s;

        // Attempt to open fileNames
        sourcePathName = pathNames[1];
        fileName = fileNames[(int)s];
        file = sourcePathName.append(fileName);

        imgThresh3.imgp = cvLoadImage(file.c_str());
        if( fileNames[s].empty() || !imgThresh3.imgp ) {
            break;
        }
    }
    s--;

    // Initialize ROI to a good starting point
    c.ROI.x = 0.15*c.imgInFull.imgp->width;
    c.ROI.y = 0.15*c.imgInFull.imgp->height;
    c.ROI.width = 0.70*c.imgInFull.imgp->width;
    c.ROI.height = 0.70*c.imgInFull.imgp->height;

    sourcePathName = pathNames[1];
    fileName = fileNames[0];
    file = sourcePathName.append(fileName);
    imgThresh0.imgp = cvLoadImage(file.c_str());

    sourcePathName = pathNames[1];
    fileName = fileNames[(int)s/3];
    file = sourcePathName.append(fileName);
    imgThresh1.imgp = cvLoadImage(file.c_str());

    sourcePathName = pathNames[1];
    fileName = fileNames[(int)2*s/3];
    file = sourcePathName.append(fileName);
    imgThresh2.imgp = cvLoadImage(file.c_str());

    sourcePathName = pathNames[1];
    fileName = fileNames[(int)s];
    file = sourcePathName.append(fileName);
    imgThresh3.imgp = cvLoadImage(file.c_str());

    return;
}


/*
  Initialize GUI for the "training" or "thresholding" procedure
  */
void initTrainingGui() {

    // Make windows
    cvNamedWindow("Input Image", CV_WINDOW_NORMAL);
    cvNamedWindow("Image 1", CV_WINDOW_NORMAL);
    cvNamedWindow("Image 2", CV_WINDOW_NORMAL);
    cvNamedWindow("Image 3", CV_WINDOW_NORMAL);
    cvNamedWindow("Image 4", CV_WINDOW_NORMAL);

    // Resize windows
    int screenWidth(1280), screenHeight(800);
    cvResizeWindow("Input Image", screenWidth/3, screenHeight);
    cvResizeWindow("Image 1", screenWidth/3, screenHeight/2);
    cvResizeWindow("Image 2", screenWidth/3, screenHeight/2);
    cvResizeWindow("Image 3", screenWidth/3, screenHeight/2);
    cvResizeWindow("Image 4", screenWidth/3, screenHeight/2);

    // Move windows
    cvMoveWindow("Input Image", 0, 0);
    cvMoveWindow("Image 1", screenWidth/3, 0);
    cvMoveWindow("Image 2", 2*screenWidth/3, 0);
    cvMoveWindow("Image 3", screenWidth/3, screenHeight/2);
    cvMoveWindow("Image 4", 2*screenWidth/3, screenHeight/2);

    // Create trackbars
    cvCreateTrackbar("C1 lower", "Input Image", &(c.R_lower), 256, trackbarHandler);
    cvCreateTrackbar("C1 Upper", "Input Image", &(c.R_upper), 256, trackbarHandler);
    cvCreateTrackbar("C2 Lower", "Input Image", &(c.G_lower), 256, trackbarHandler);
    cvCreateTrackbar("C2 Upper", "Input Image", &(c.G_upper), 256, trackbarHandler);
    cvCreateTrackbar("C3 Lower", "Input Image", &(c.B_lower), 256, trackbarHandler);
    cvCreateTrackbar("C3 Upper", "Input Image", &(c.B_upper), 256, trackbarHandler);
    cvCreateTrackbar("Color Space", "Input Image", &c.colorSpace, 1, trackbarHandlerSpecial);

    // Set mouse callback
    cvSetMouseCallback("Input Image", onMouse, 0);

    return;
}


/*
  Initialize GUI for modifying internal parameters which have to do with dot identification based on dot geometry
  */
void initTrainingGui2() {

    // Destroy and regenerate window
    cvDestroyWindow("Input Image");
    cvNamedWindow("Input Image", CV_WINDOW_NORMAL);

    // Resize and move window
    int screenWidth(1280), screenHeight(800);
    cvResizeWindow("Input Image", screenWidth/3, screenHeight);
    cvMoveWindow("Input Image", 0, 0);

    // Create trackbars for blob thresholding
    cvCreateTrackbar("Static Migration Threshold", "Input Image", &(c.blob_dist_thresh), 100, trackbarHandler);
    cvCreateTrackbar("Area Threshold", "Input Image", &(c.area_thresh), 500, trackbarHandler);

    return;
}


/*
  Initialize GUI for normal code execution
  */
void initGui() {

    // Initialize
    int screenWidth(1280), screenHeight(800);

    // Make windows
    cvNamedWindow("Processed Image", CV_WINDOW_NORMAL);
    cvNamedWindow("XX Strain Field Overlay", CV_WINDOW_NORMAL);
    cvNamedWindow("XY Strain Field Overlay", CV_WINDOW_NORMAL);
    cvNamedWindow("YY Strain Field Overlay", CV_WINDOW_NORMAL);

    // Resize windows
    cvResizeWindow("Processed Image", screenWidth/3, screenHeight/2);
    cvResizeWindow("XX Strain Field Overlay", screenWidth/3, screenHeight/3);
    cvResizeWindow("XY Strain Field Overlay", screenWidth/3, screenHeight/3);
    cvResizeWindow("YY Strain Field Overlay", screenWidth/3, screenHeight/3);

    // Move windows
    cvMoveWindow("Processed Image", 1*screenWidth/6, screenHeight/4);
    cvMoveWindow("XX Strain Field Overlay", screenWidth/2, 0);
    cvMoveWindow("XY Strain Field Overlay", screenWidth/2, screenHeight/3);
    cvMoveWindow("YY Strain Field Overlay", screenWidth/2, 2*screenHeight/3);

    return;
}


/*
  Trackbar handler
  */
void trackbarHandler(int pos)
{
    return;
}


/*
  Special trackbar handler for the trackbar which selects the color thresholds
  */
void trackbarHandlerSpecial(int pos) {

    // Change color spaces if requested
    if(pos != prevPos) {

        if(pos == 0) {

            // Hold threshold values currently associated with trackbars
            int c1LProxy = c.H_lower;
            int c1UProxy = c.H_upper;
            int c2LProxy = c.S_lower;
            int c2UProxy = c.S_upper;
            int c3LProxy = c.V_lower;
            int c3UProxy = c.V_upper;

            // Re-generate the trackbars, linking them with different pointers
            cvCreateTrackbar("C1 lower", "Input Image", &(c.R_lower), 256, trackbarHandler);
            cvCreateTrackbar("C1 Upper", "Input Image", &(c.R_upper), 256, trackbarHandler);
            cvCreateTrackbar("C2 Lower", "Input Image", &(c.G_lower), 256, trackbarHandler);
            cvCreateTrackbar("C2 Upper", "Input Image", &(c.G_upper), 256, trackbarHandler);
            cvCreateTrackbar("C3 Lower", "Input Image", &(c.B_lower), 256, trackbarHandler);
            cvCreateTrackbar("C3 Upper", "Input Image", &(c.B_upper), 256, trackbarHandler);

            // Set the held values into the object array
            c.H_lower = c1LProxy;
            c.H_upper = c1UProxy;
            c.S_lower = c2LProxy;
            c.S_upper = c2UProxy;
            c.V_lower = c3LProxy;
            c.V_upper = c3UProxy;

        } else {

            // Hold threshold values currently associated with trackbars
            int c1LProxy = c.R_lower;
            int c1UProxy = c.R_upper;
            int c2LProxy = c.G_lower;
            int c2UProxy = c.G_upper;
            int c3LProxy = c.B_lower;
            int c3UProxy = c.B_upper;

            // Re-generate the trackbars, linking them with different pointers
            cvCreateTrackbar("C1 lower", "Input Image", &(c.H_lower), 256, trackbarHandler);
            cvCreateTrackbar("C1 Upper", "Input Image", &(c.H_upper), 256, trackbarHandler);
            cvCreateTrackbar("C2 Lower", "Input Image", &(c.S_lower), 256, trackbarHandler);
            cvCreateTrackbar("C2 Upper", "Input Image", &(c.S_upper), 256, trackbarHandler);
            cvCreateTrackbar("C3 Lower", "Input Image", &(c.V_lower), 256, trackbarHandler);
            cvCreateTrackbar("C3 Upper", "Input Image", &(c.V_upper), 256, trackbarHandler);

            // Set the held values into the object array
            c.R_lower = c1LProxy;
            c.R_upper = c1UProxy;
            c.G_lower = c2LProxy;
            c.G_upper = c2UProxy;
            c.B_lower = c3LProxy;
            c.B_upper = c3UProxy;

        }

    }

    // Track changes in trackbar position
    prevPos = pos;

    return;
}


/*
  Handle mouse clicking
  */
void onMouse(int event, int x, int y, int flags, void *param) {

    // Handle down and up clicks, for click-and-drag ROI rectangle
    if(event == CV_EVENT_LBUTTONDOWN) {

        // Assign ROI location
        c.ROI.x = x;
        c.ROI.y = y;

    } else if(event == CV_EVENT_LBUTTONUP) {

        // Assign ROI dimensions
        c.ROI.width = x - c.ROI.x;
        c.ROI.height = y - c.ROI.y;

        // Display ROI visually
        imgInFull = cvCloneImage(c.imgInFull.imgp);
        CvScalar col;
        if(imgInFull->nChannels == 1) {
            col = cvScalar(255);
        } else {
            col = cvScalar(0, 0, 255);
        }
        cvDrawRect(imgInFull, cvPoint(c.ROI.x, c.ROI.y), cvPoint(c.ROI.x + c.ROI.width, c.ROI.y + c.ROI.height), col);
        cvShowImage("Input Image", imgInFull);
    }

    return;
}


///*
//  Load thresholding values from file into memory

//  @input sourcePathName = Absolute path where file is located
//  */
//void loadPersistentValues(string sourcePathName) {

//    // Look for previously saved threshold values; if they exist, then load them into memory
//    string fileName = "persistentValues.dat";
//    string file = sourcePathName.append(fileName);
//    ifstream persistFile( file.c_str(), ios::in );

//    if(persistFile.is_open()) {

//        // Load values for the persistent parameters
//        persistFile.seekg(0);

//        persistFile >> c.R_lower;
//        persistFile >> c.R_upper;
//        persistFile >> c.G_lower;
//        persistFile >> c.G_upper;
//        persistFile >> c.B_lower;
//        persistFile >> c.B_upper;

//        persistFile >> c.H_lower;
//        persistFile >> c.H_upper;
//        persistFile >> c.S_lower;
//        persistFile >> c.S_upper;
//        persistFile >> c.V_lower;
//        persistFile >> c.V_upper;

//        persistFile.close();
//    }


//    return;
//}










/*
  this code allows user to modify internal thresholds, etc.
//

// Confirm identified dots
//   This is the user feedback loop. The user examines the output image, which clearly shows identified dots. They then
//     have the ability to change all parameters which affect dot identification.
//   Note that dots are ONLY recognized if their probability is above a threshold.
dotsIdentified = "n";
while(dotsIdentified.compare("n") == 0)
{
    // Refresh image
    outputImage();

    // Request user confirmation that dots have been correctly identified
    std::cout << endl;
    std::cout << "Open an image viewer for the most updated version of the file " << destPathName << "outputImage.bmp" << endl;
    std::cout << endl;
    std::cout << "In this image, do the blobs appear to be correctly identified (y/n)? ";
    std::cin >> dotsIdentified;
    std::cout << endl;

    // Allow user to change all parameters affecting dot identification
    if(dotsIdentified.compare("n") == 0)
    {
        std::cout << "Please select an option: " << endl;
        std::cout << "  1. Area threshold" << endl;
        std::cout << "  2. Probability threshold" << endl;
        std::cout << endl;
        std::cin >> option;

        // Area threshold
        if(option.compare("1") == 0) {
            std::cout << "Current area threshold: " << area_thresh << endl;
            std::cout << "Minimum blob area: " << min_blob_area << endl;
            std::cout << "Maximum blob area: " << max_blob_area << endl;
            std::cout << "New area threshold: ";
            std::cin >> area_thresh;
            std::cout << endl;
        }

        // Probability threshold
        else if(option.compare("1") == 0) {
            std::cout << "Current probabilty threshold: " << dotProbabilityThresh << endl;
            std::cout << "New area threshold: ";
            std::cin >> dotProbabilityThresh;
            std::cout << endl;
        }
    }
    else {
        break;
    }

    // Attempt to identify the dots using the new threshold and logic values
    identifyDots();
}
*/


